package game.sprite;

import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Map;
import javax.imageio.ImageIO;


/**
 * Superclass for all game objects that are drawn.
 * It includes a spriteSet which is a array of images that
 * can represent different states a sprite is in.
 * This is retrieved from a set stored in GameResources while the splash
 * screen is loading.
 * 
 * @author Ben Schwab
 * @author rwb10
 * 
 */
public abstract class Sprite {

    private Map<String, Image> mySpriteSet;
    private boolean myIsActive;
    private String myActiveState;
    private double myX;
    private double myY;
    private double myScaleFactor;
    private double myRotation;

    /**
     * Constructor.
     * 
     * @param startLocation Start location of sprite.
     * @param spriteSet Default image string name of sprite.
     * @param isActive If sprite is currently alive on level.
     */
    public Sprite (Point startLocation, String spriteSet, boolean isActive) {
        myIsActive = isActive;
        myScaleFactor = 1;
        myRotation = 0;

    }

    /**
     * Loads an image.
     * 
     * @param imageName location of image
     * @return the image.
     */
    public Image loadImage (String imageName) {
        BufferedImage img = null;
        URL path = getClass().getResource(imageName);
        try {
            try {
                img = ImageIO.read(new File(path.toURI()));
            }
            catch (URISyntaxException e) {
                System.out.println(
                        "Something went really wrong loading the image: " + path);
                e.printStackTrace();
            }
        }
        catch (IOException e) {
            System.out.println(
                    "Something went wrong loading the image: " + path);
        }
        return img;
    }

    /**
     * Set the sprite to a proper map of sprite states to sprite images.
     * @param spriteSet Map of strings to images.
     */
    public void setSpriteSet (Map<String, Image> spriteSet) {
        mySpriteSet = spriteSet;
    }

    /**
     * set the center coordinate of the sprite
     * 
     * @param center coordinate of the sprite
     */
    public void setCenterCoordinate (Point center) {
        myX = center.getX() - getWidth() / 2;
        myY = center.getY() - getHeight() / 2;
    }

    /**
     * Get the center coordinate of the sprite
     * 
     * @return
     */
    public Point getCenterCoordinate () {
        return new Point((int) (getX() + getWidth() / 2),
                (int) (getY() + getHeight() / 2));
    }

    /**
     * return the rectangle that bounds the active image of the sprite
     * 
     * @return
     */
    public Rectangle getBounds () {
        return new Rectangle((int) getX(), (int) getY(), getWidth(),
                getHeight());
    }

    /**
     * 
     * @return the height of the active image of the sprite
     */
    public int getHeight () {
        return (int) (getActiveImage().getWidth(null) * myScaleFactor);
    }

    /**
     * 
     * @return the width of the active image being drawn for this sprite
     */
    public int getWidth () {
        return (int) (getActiveImage().getWidth(null) * myScaleFactor);
    }

    /**
     * Update this sprite. It is up to the sprites implementation to define what
     * this does
     */
    public abstract void update ();

    /**
     * Draw the sprite on the board based on its current conditions.
     * @param pen Graphics to draw with.
     */
    public void draw (Graphics2D pen) {
        if (getIsActive()) {
            Point transformedPoint = Background.transformPoint(new Point(
                    (int) getX(), (int) getY()));
            Point transformedCenterPoint = 
                    Background.transformPoint(getCenterCoordinate());
            AffineTransform rotationTransform = new AffineTransform();
            rotationTransform.rotate(Math.toRadians(myRotation),
                    transformedCenterPoint.getX(),
                    transformedCenterPoint.getY());
            AffineTransform savedTransform = pen.getTransform();
            pen.setTransform(rotationTransform);
            pen.drawImage(getActiveImage(), (int) transformedPoint.getX(),
                    (int) transformedPoint.getY(), getHeight(), getWidth(),
                    null);
            pen.setTransform(savedTransform);
            // System.out.println(transformedPoint.toString());
        }
    }

    /**
     * Attempts to change the image for a sprite state. If the state does not
     * exist for this sprite, the state
     * remains the same
     * 
     * @param stateName String label of desired image.
     */
    public void setActiveImage (String stateName) {
        if (mySpriteSet.containsKey(stateName)) {
            myActiveState = stateName;
        }
    }

    /**
     * 
     * @return the active image for this sprite
     */
    public Image getActiveImage () {
        return mySpriteSet.get(myActiveState);
    }

    /**
     * 
     * @return whether or not the sprite is active in the game.
     */
    public boolean getIsActive () {
        return myIsActive;
    }

    /**
     * Set the active state of this sprite.
     * 
     * @param isActive true if active, false if inactive
     */
    public void setActive (boolean isActive) {
        myIsActive = isActive;
        System.out.println(isActive);
    }

    /**
     * Resize the sprite
     * 
     * @param scaleFactor Factor to scale the drawing of the sprite. Center
     *        location of the sprite remains fixed.
     */
    public void resize (double scaleFactor) {
        myScaleFactor = scaleFactor;
    }

    /**
     * 
     * @return returns the scale factor.
     */
    public double getScaleFactor () {
        return myScaleFactor;
    }

    /**
     * Set the rotation degree of the sprite
     * 
     * @param rotationDegrees the amount of the rotation in degrees
     */
    public void setRotation (double rotationDegrees) {
        myRotation = rotationDegrees;
    }

    /**
     * 
     * @return Get left x of the sprite.
     */
    public double getX () {
        return myX;
    }

    /**
     * 
     * @return Get the top y of the sprite
     */
    public double getY () {
        return myY;
    }

    /**
     * 
     * @param x new left x of sprite
     */
    public void setX (double x) {
        myX = x;
    }

    /**
     * 
     * @param y new top y of sprite
     */
    public void setY (double y) {
        myY = y;
    }

    /**
     * Move the top left corner of the sprite to specified coordinate.
     * 
     * @param corner top left coordinate to move sprite to
     */
    public void setTopLeftCoordinate (Point corner) {
        setX(corner.getX());
        setY(corner.getY());
    }

    /**
     * 
     * @param other Sprite to check for collision with.
     * @return true if they collide.
     */
    public boolean intersects (Sprite other) {
        return other.getBounds().intersects(getBounds());
    }

    /**
     * 
     * @return returns the current angle of the sprite.
     */
    public double getRotation () {

        return myRotation;
    }

}
